/************************************************************************
 *
 * i16.h
 *
 * (c) Copyright 1998-2004 Analog Devices, Inc.  All rights reserved.
 * $Revision: 1.1 $
 ************************************************************************/

/*
 * Support for 16-bit integer operations (2x16/4x16), including
 * arithmetic, packing and unpacking, combining and extracting.
 *
 * Portable implementations from i16.c can be used
 * when the __NO_BUILTIN macro is defined.
 */

#pragma once
#ifndef __NO_BUILTIN
#pragma system_header /* i16.h */
#endif

#ifndef __I16_DEFINED
#define __I16_DEFINED

/*
 * The functions declared in this file perform arithmetic on the int2x16
 * and int4x16 datatypes.  The int2x16 datatype consists of two 16-bit
 * integers packed into a 32-bit structure.  The int4x16 datatype
 * consists of four 16-bit integers packed into a 64-bit item.
 *
 * We use the notation {A,B} to represent an int2x16 containing the two
 * 16-bit values A and B.  The TigerSharc is a little endian
 * architecture, so the "first" element is stored in the low order bits.
 * We consider A to be the "first" element of {A,B}, so that the
 * representation in a 32-bit register is as follows:
 *
 *    31                 16 15                  0
 *    +-----------------------------------------+
 *    |       Value B      |       Value A      |
 *    |Bit 15 ....... Bit 0|Bit 15 ....... Bit 0|
 *    +-----------------------------------------+
 *
 * Similarly, we use {A,B,C,D} to represent an int4x16 containing the
 * 16-bit values A, B, C, and D, with A in the lowest order bits, B next,
 * followed by C, and D in the highest order bits.  (This means that
 * C and D will be stored in a register or memory location numbered one
 * higher than A and B.)
 *
 * Most operations on these types are elementwise.  For example, the add
 * operation for int4x16 takes the first 16-bit quantity from each
 * argument and adds them together to produce the first 16-bit quantity
 * in the result.  Likewise, the second 16-bit elements are added to
 * produce the second element of the result.  For example, the int2x16
 * add operation on arguments {A,B} and {C,D} produces a result
 * {A+C,B+D}.
 *
 * A few operations which are not elementwise are also supported.  The
 * sideways sum operation adds up all of the 16-bit elements in its
 * single argument.  A variety of packing and unpacking operations for
 * accessing the individual elements of an int2x16 or int4x16 is also
 * available.
 *
 * These operations are very efficient for TigerSharc because they are
 * directly supported in hardware.  Most are implemented as a single
 * machine instruction; a few which will take multiple machine
 * instructions are provided for convenience.
 *
 * If a portable implementation is desired, another definition can be 
 * supplied for the underlying implementation types and _RAW_TYPES defined
 * prior to inclusion of this file. 
 */

#ifdef __ADSPTS__

#ifndef _RAW_TYPES
#define _RAW_TYPES
typedef int           _raw32;
typedef long long int _raw64;
#endif

#endif /* __ADSPTS__ */


typedef _raw32  int2x16;
typedef _raw64  int4x16;
typedef _raw64  int2x32;


/* Some functions are available as builtins */
#if defined(__ADSPTS__) && !defined(__NO_BUILTIN)

#define add_i2x16  __builtin_add_2x16
#define sub_i2x16  __builtin_sub_2x16
#define mult_i2x16 __builtin_mult_i2x16
#define sum_i2x16  __builtin_sum_2x16

#define compact_to_i2x16 __builtin_compact_to_i2x16
#define expand_i2x16 __builtin_expand_i2x16

#define add_i4x16  __builtin_add_4x16
#define sub_i4x16  __builtin_sub_4x16
#define mult_i4x16 __builtin_mult_i4x16
#define sum_i4x16  __builtin_sum_4x16

#define compose_64 __builtin_compose_64
#define low_32 __builtin_low_32
#define high_32 __builtin_high_32

#endif /* __ADSPTS__ && !__NO_BUILTIN */


#ifdef __cplusplus
extern "C" {
#endif

/*************************************************************************
 * Operations on int2x16: Two 16-bit integers in parallel
 *************************************************************************/

/* Addition:  add_i2x16({A,B}, {X,Y}) = {A+X,B+Y} */
int2x16 add_i2x16(int2x16 x, int2x16 y);

/* Subtraction:  sub_i2x16({A,B}, {X,Y}) = {A-X,B-Y} */
int2x16 sub_i2x16(int2x16 x, int2x16 y);

/* Multiplication: mult_i2x16({A,B}, {X,Y}) = {A*X,B*Y} */
int2x16 mult_i2x16(int2x16 x, int2x16 y);

/* Sideways sum: sum_i2x16({A,B}) = A+B
   Note that this operation produces a single 32-bit integer result. */
int sum_i2x16(int2x16 x);


/* Packing and unpacking */

/* Compact a single 2x32-bit integer value into a 2x16 structure.
   The high order 16 bits of each original 32-bit value are lost.
       compact_to_i2x16({A_32,B_32}) = {A_16,B_16} */
int2x16 compact_to_i2x16(int2x32 input);

/* Compact two 32-bit integer values into a 2x16 structure.
   The high order 16 bits of each original 32-bit value are lost.
       compact_to_i2x16_from_i32(A_32, B_32) = {A_16,B_16} */
int2x16 compact_to_i2x16_from_i32(int low, int high);

/* combine two independent inputs into a 2x32; then use the compact builtin */
#define compact_to_i2x16_from_i32(low, high) \
	compact_to_i2x16(compose_64(low, high))


/* Expand a 2x16 integer structure into a 2x32 structure.
   Each 16-bit element is sign extended to 32 bits.
       expand_i2x16({A_16,B_16}) = {A_32,B_32} */
int2x32 expand_i2x16(int2x16 input);

/* Expand a 2x16 integer structure into 2 32-bit integer variables.
   The second and third arguments are pointers to the locations where
   the results should be stored.  The first argument is referenced twice,
   so it should not have any side effects.
       expand_i2x16_to_i32({A_16,B_16}, x, y) =>  *x = A_32; *y = B_32; */
void expand_i2x16_to_i32(int2x16 input, int *low_out, int *high_out);

#define expand_i2x16_to_i32(input, low_out, high_out) \
	(*(low_out)  = low_32(expand_i2x16(input)) ,\
	 *(high_out) = high_32(expand_i2x16(input)) )

/* Extract half of a 2x16 integer structure.
   The extracted value is sign extended to 32 bits.
       expand_low_of_i2x16({A_16,B_16})  = A_32
       expand_high_of_i2x16({A_16,B_16}) = B_32 */
int expand_low_of_i2x16(int2x16 input);
int expand_high_of_i2x16(int2x16 input);

#define expand_low_of_i2x16(input) \
	 low_32(expand_i2x16(input))
#define expand_high_of_i2x16(input) \
	 high_32(expand_i2x16(input)) 




/*************************************************************************
 * Operations on int4x16: Four 16-bit integers in parallel
 *************************************************************************/

/* Addition:  add_i4x16({A,B,C,D}, {W,X,Y,Z}) = {A+W,B+X,C+Y,D+Z} */
int4x16 add_i4x16(int4x16 x, int4x16 y);

/* Subtraction:  sub_i4x16({A,B,C,D}, {W,X,Y,Z}) = {A-W,B-X,C-Y,D-Z} */
int4x16 sub_i4x16(int4x16 x, int4x16 y);

/* Subtraction:  mult_i4x16({A,B,C,D}, {W,X,Y,Z}) = {A*W,B*X,C*Y,D*Z} */
int4x16 mult_i4x16(int4x16 x, int4x16 y);

/* Sideways sum: sum_i4x16({A,B,C,D}) = A+B+C+D
   Note that this operation produces a single 32-bit integer result. */
int sum_i4x16 (int4x16 x);


/* Packing and unpacking */

/* Compact four 32-bit integer values into a 4x16 structure.
   The high order 16 bits of each original 32-bit value are lost.
       compact_to_i4x16_from_i32(A_32, B_32, C_32, D_32) =
          {A_16,B_16,C_16,D_16} */
int2x16 compact_to_i4x16_from_i32(int llo, int lhi, int hlo, int hhi);

#define compact_to_i4x16_from_i32(llo, lhi, hlo, hhi) \
	compose_i4x16_from_i2x16(			     \
	    compact_to_i2x16_from_i32(llo, lhi),   \
	    compact_to_i2x16_from_i32(hlo, hhi))

/* Create a 4x16 integer structure from two 2x16 integer structures.
       compose_i4x16_from_i2x16({A,B}, {C,D}) = {A,B,C,D} */
int4x16 compose_i4x16_from_i2x16(int2x16 low, int2x16 high);

#define compose_i4x16_from_i2x16(a, b)  compose_64(a, b)


/* Extract the two high or two low values from a 4x16 integer structure
   to produce an int2x16.
       extract_low_of_i4x16({A,B,C,D})  = {A,B}
       extract_high_of_i4x16({A,B,C,D}) = {C,D} */
int2x16 extract_low_of_i4x16(int4x16 input);
int2x16 extract_high_of_i4x16(int4x16 input);

#define extract_low_of_i4x16(a)  low_32(a)
#define extract_high_of_i4x16(a)  high_32(a)

/* Expand a 4x16 integer structure into 4 32-bit integer variables.
   The last four arguments are pointers to the locations where the
   results should be stored.  The first argument is referenced multiple
   times, so it should not have any side effects.
       expand_i4x16_to_i32({A_16,B_16,C_16,D_16}, w, x, y, z) =>
          *w = A_32; *x = B_32; *y = C_32; *z = D_32 */
void expand_i4x16_to_i32(int4x16 input,
			 int *llo, int *lhi, int *hlo, int *hhi);

#define expand_i4x16_to_i32(full, llo, lhi, hlo, hhi) \
     (expand_i2x16_to_i32(extract_low_of_i4x16(full) , llo, lhi),  \
      expand_i2x16_to_i32(extract_high_of_i4x16(full), hlo, hhi))



/*************************************************************************
 * Packing and unpacking primitives 
 *************************************************************************/

/* These operations do primitive packing and unpacking at the machine
   word level, and therefore have no intrinsic type.  They can operate
   on any objects of the appropriate size: either integers, fractional
   values, or containers such as int2x16 and int4x16. */

/* Compose two 32-bit values into a single 64-bit value. */
_raw64 compose_64(_raw32 low, _raw32 high);

/* Extract the low order 32 bits from a 64-bit value. */
_raw32 low_32(_raw64 source);

/* Extract the low order 32 bits from a 64-bit value. */
_raw32 high_32(_raw64 source);

 
#ifdef __cplusplus
}
#endif

#endif /* __I16_DEFINED */
